// ======================================================================
// Copyright (C) 2013 Hell-Prototypes. / www.hellprototypes.com
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// ======================================================================
`timescale 10ns/1ns
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    16:29:29 06/09/2012 
// Design Name: 
// Module Name:    Core 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////

module Core(
		input  	CLK_Core,
		input  	nRST,
		
		input   [31:0] cmd_param,
		
		output reg [15:0] FIFO_DIN,
		output reg       FIFO_WR_EN,
		input            FIFO_FULL,
	
		input   LS_RD_ADC,

		output [7:0] Logic_Out,
		input  [7:0] Logic_In
	);

parameter 
	IDLE	= 3'b000,
	S1		= 3'b001,
	S2		= 3'b010,
	S3		= 3'b011,
	S4		= 3'b100,
	S5		= 3'b101,
	S6		= 3'b110,
	S7		= 3'b111;

reg [15:0] next_FIFO_DIN;
reg       next_FIFO_WR_EN;

reg [2:0]  state, next_state;
reg [15:0] division_top, next_division_top;
reg [15:0] division, next_division;
reg adc_clk, next_adc_clk;
reg adc_read_en, next_adc_read_en;

reg [7:0] last_filtered_value, next_last_filtered_value;
reg [7:0] rle_count, next_rle_count;

reg [7:0] filter_mem[0:7];
reg [2:0] loop_pointer, next_loop_pointer;

wire [9:0]filter_sum1 = {2'b00, filter_mem[0]} + {2'b00, filter_mem[1]} + {2'b00, filter_mem[2]} + {2'b00, filter_mem[3]};
wire [9:0]filter_sum2 = {2'b00, filter_mem[4]} + {2'b00, filter_mem[5]} + {2'b00, filter_mem[6]} + {2'b00, filter_mem[7]};
wire [10:0]filter_sum  = {1'b0, filter_sum1} + {1'b0, filter_sum2};

wire [7:0]filtered_value =  filter_sum[10:3];

wire mismatch = |(filtered_value ^ last_filtered_value);
wire rle_count_full = &rle_count;

assign Logic_Out[7]   = adc_clk;
assign Logic_Out[6]   = 0;
assign Logic_Out[5]   = 0;
assign Logic_Out[4]   = &filter_sum[2:0];
assign Logic_Out[3]   = FIFO_WR_EN;
assign Logic_Out[2]   = |cmd_param[31:16]; //FIXME
assign Logic_Out[1]   = mismatch;
assign Logic_Out[0]   = adc_read_en ^ FIFO_FULL;

integer i;
initial begin
  for (i=0; i<8; i=i+1) filter_mem[i] = 8'h00;
end
//=============================================================================
always @(posedge CLK_Core or negedge nRST) begin
	if(!nRST) begin
		state <= 0;
		FIFO_DIN <= 0;
		FIFO_WR_EN <= 0;

		adc_clk <= 0;
		adc_read_en <= 0;
		division <= 0;
		division_top <= 0;
		last_filtered_value <= 8'h00;
		rle_count <= 8'h00;
		loop_pointer <= 3'b000;
	end else begin
		state <= next_state;
		FIFO_DIN  <= next_FIFO_DIN;
		FIFO_WR_EN <= next_FIFO_WR_EN;

		adc_clk <= next_adc_clk;
		adc_read_en <= next_adc_read_en;
		division <= next_division;
		division_top <= next_division_top;
		last_filtered_value <= next_last_filtered_value;
		rle_count <= next_rle_count;
		loop_pointer <= next_loop_pointer;
		filter_mem[loop_pointer] <= Logic_In;
	end
end

always @* begin	
	next_state = state;
	next_adc_clk = adc_clk;
	next_adc_read_en = adc_read_en;
	next_last_filtered_value = last_filtered_value;
	next_division_top = division_top;
	next_rle_count = rle_count;
	next_loop_pointer = loop_pointer + 1;

	if(division == division_top) begin
		next_division = 0;
	end else begin
		next_division = division + 1;
	end

	/* Read status */
	next_FIFO_DIN  = FIFO_DIN;
	next_FIFO_WR_EN = 0;
	
	if(adc_read_en) begin
		if(~|division) begin
			next_adc_clk = 1;
		end
		if((division == 16'h0001) && (!FIFO_FULL)) begin
			next_adc_clk = 0;
			next_last_filtered_value = filtered_value;
			if(mismatch || rle_count_full) begin
				next_FIFO_DIN = {rle_count, last_filtered_value};
				next_FIFO_WR_EN = 1;
				next_rle_count = 8'h00;
			end else begin
				next_rle_count = rle_count + 1;
			end
		end
	end else begin
		if (LS_RD_ADC) begin
			next_adc_read_en = 1;
			next_division_top = cmd_param[15:0];//ADC sample freq = CLK_Core/(division_top + 1)
		end
	end
end

endmodule
